/// @file artillery_game.cc
/// This example demonstrates loading, running and scripting a very simple NaCl
/// module.  To load the NaCl module, the browser first looks for the
/// CreateModule() factory method (at the end of this file).  It calls
/// CreateModule() once to load the module code from your .nexe.  After the
/// .nexe code is loaded, CreateModule() is not called again.
///
/// Once the .nexe code is loaded, the browser than calls the CreateInstance()
/// method on the object returned by CreateModule().  It calls CreateInstance()
/// each time it encounters an <embed> tag that references your NaCl module.
///
/// The browser can talk to your NaCl module via the postMessage() Javascript
/// function.  When you call postMessage() on your NaCl module from the browser,
/// this becomes a call to the HandleMessage() method of your pp::Instance
/// subclass.  You can send messages back to the browser by calling the
/// PostMessage() method on your pp::Instance.  Note that these two methods
/// (postMessage() in Javascript and PostMessage() in C++) are asynchronous.
/// This means they return immediately - there is no waiting for the message
/// to be handled.  This has implications in your program design, particularly
/// when mutating property values that are exposed to both the browser and the
/// NaCl module.

#include "artillery_game.h"

#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <sstream>
#include <string>
#include "ppapi/cpp/completion_callback.h"
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/var.h"

using namespace std;


void MakeLandScape(vector<int> &height, int MaxHeight)
{
	int size=height.size();
	int height1 = rand() % MaxHeight + 1;
	int length1 = size/4;
	int height2 = rand() % MaxHeight + 1;
	int length2 = size*3/4;
	int height3 = rand() % MaxHeight + 1;
	int length3 = size;

	for(int x=0;x<length1;x++)
	{
		height[x]=height1;
	}
	for(int y=length1;y<length2;y++)
	{
		height[y]=height2;
	}
	for (int z=length2;z<length3;z++)
	{
		height[z]=height3;
	}
}

namespace
{


int getrandomnumber()
{
	int r = rand() % 10 + 1;
	return r;
}

const int kPthreadMutexSuccess = 0;
const char* const kPaintMethodId = "paint";
const double kInvalidPiValue = -1.0;
const int kMaxPointCount = 1000000000; // The total number of points to draw.
const uint32_t kOpaqueColorMask = 0xff000000; // Opaque pixels.
const uint32_t kRedMask = 0xff0000;
const uint32_t kBlueMask = 0xff;
const uint32_t kRedShift = 16;
const uint32_t kBlueShift = 0;
const uint32_t kblue = kOpaqueColorMask |0x0000ff;
const uint32_t kgreen = kOpaqueColorMask |0x00ff00;
const uint32_t kred = kOpaqueColorMask |0xff0000;
const uint32_t kwhite = kOpaqueColorMask |0xffffff;

// This is called by the browser when the 2D context has been flushed to the
// browser window.
void FlushCallback(void* data, int32_t result)
{
	static_cast<artillery_game::ArtilleryGame*>(data)->set_flush_pending(false);
}

void PaintCallback(void *data, int32_t result)
{
	static_cast<artillery_game::ArtilleryGame*>(data)->Paint();
}

} // namespace

namespace artillery_game
{

// A small helper RAII class that implements a scoped pthread_mutex lock.
class ScopedMutexLock
{
public:
	explicit ScopedMutexLock(pthread_mutex_t* mutex) :
			mutex_(mutex)
	{
		if (pthread_mutex_lock(mutex_) != kPthreadMutexSuccess)
		{
			mutex_ = NULL;
		}
	}
	~ScopedMutexLock()
	{
		if (mutex_)
			pthread_mutex_unlock(mutex_);
	}
	bool is_valid() const
	{
		return mutex_ != NULL;
	}
private:
	pthread_mutex_t* mutex_; // Weak reference.
};

// A small helper RAII class used to acquire and release the pixel lock.
class ScopedPixelLock
{
public:
	explicit ScopedPixelLock(ArtilleryGame* image_owner) :
			image_owner_(image_owner), pixels_(image_owner->LockPixels())
	{
	}

	~ScopedPixelLock()
	{
		pixels_ = NULL;
		image_owner_->UnlockPixels();
	}

	uint32_t* pixels() const
	{
		return pixels_;
	}
private:
	ArtilleryGame* image_owner_; // Weak reference.
	uint32_t* pixels_; // Weak reference.

	ScopedPixelLock(); // Not implemented, do not use.
};


ArtilleryGame::ArtilleryGame(PP_Instance instance) :
		pp::Instance(instance), graphics_2d_context_(NULL), pixel_buffer_(NULL), flush_pending_(
				false), quit_(false), fire_shell_thread_(0), pi_(0.0)
{
	pthread_mutex_init(&pixel_buffer_mutex_, NULL);
}

ArtilleryGame::~ArtilleryGame()
{
	quit_ = true;
	if (fire_shell_thread_)
	{
		pthread_join(fire_shell_thread_, NULL);
	}
	DestroyContext();
	// The ComputePi() thread should be gone by now, so there is no need to
	// acquire the mutex for |pixel_buffer_|.
	delete pixel_buffer_;
	pthread_mutex_destroy(&pixel_buffer_mutex_);
}

void ArtilleryGame::DidChangeView(const pp::Rect& position,
		const pp::Rect& clip)
{
	if (position.size().width() == width()
			&& position.size().height() == height())
		return; // Size didn't change, no need to update anything.

	// Create a new device context with the new size.
	DestroyContext();
	CreateContext(position.size());
	// Delete the old pixel buffer and create a new one.
	ScopedMutexLock scoped_mutex(&pixel_buffer_mutex_);
	delete pixel_buffer_;
	pixel_buffer_ = NULL;
	if (graphics_2d_context_ != NULL)
	{
		pixel_buffer_ = new pp::ImageData(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
				graphics_2d_context_->size(), false);
	}
}

bool ArtilleryGame::Init(uint32_t argc, const char* argn[], const char* argv[])
{
	srand((unsigned int) time(NULL));
	pthread_create(&fire_shell_thread_, NULL, FireShell, this);
	return true;
}

uint32_t* ArtilleryGame::LockPixels()
{
	void* pixels = NULL;
	// Do not use a ScopedMutexLock here, since the lock needs to be held until
	// the matching UnlockPixels() call.
	if (pthread_mutex_lock(&pixel_buffer_mutex_) == kPthreadMutexSuccess)
	{
		if (pixel_buffer_ != NULL && !pixel_buffer_->is_null())
		{
			pixels = pixel_buffer_->data();
		}
	}
	return reinterpret_cast<uint32_t*>(pixels);
}

void ArtilleryGame::HandleMessage(const pp::Var& var_message)
{
	if (!var_message.is_string())
	{
		PostMessage(pp::Var("Cannot handle messages that are not strings"));
	}
	std::string message = var_message.AsString();
	if (message == kPaintMethodId)
	{
		//pthread_create(&fire_shell_thread_, NULL, FireShell, this);
//		PostMessage("Lauched thread");
		//Paint();
	}
	else
	{
		//Paint();
		PostMessage(var_message);
	}

	/*    string message_string;
	 stringstream message;

	 int tank_1 = getrandomnumber();
	 int tank_2 = getrandomnumber();
	 message<<" tank 1:"<<tank_1;
	 message<<" tank 2:"<<tank_2;
	 if (tank_1 < tank_2)
	 message<<" Tank 2 wins!";
	 if (tank_1 > tank_2)
	 message<<" Tank 1 wins!";
	 if (tank_1 == tank_2)
	 message<<" You blew each other up. Tie.";

	 message_string = message.str();
	 pp::Var return_var = message_string;
	 PostMessage(return_var);*/

	/*string message_string;
	 stringstream message;
	 message << getrandomnumber();
	 message_string = message.str();
	 pp::Var return_var = message_string;
	 PostMessage(return_var);*/
}

void ArtilleryGame::UnlockPixels() const
{
	pthread_mutex_unlock(&pixel_buffer_mutex_);
}

void ArtilleryGame::Paint()
{
	ScopedMutexLock scoped_mutex(&pixel_buffer_mutex_);
	if (!scoped_mutex.is_valid())
	{
		return;
	}
	FlushPixelBuffer();
}

void ArtilleryGame::CreateContext(const pp::Size& size)
{
	ScopedMutexLock scoped_mutex(&pixel_buffer_mutex_);
	if (!scoped_mutex.is_valid())
	{
		return;
	}
	if (IsContextValid())
		return;
	graphics_2d_context_ = new pp::Graphics2D(this, size, false);
	if (!BindGraphics(*graphics_2d_context_))
	{
		printf("Couldn't bind the device context\n");
	}
}

void ArtilleryGame::DestroyContext()
{
	ScopedMutexLock scoped_mutex(&pixel_buffer_mutex_);
	if (!scoped_mutex.is_valid())
	{
		return;
	}
	if (!IsContextValid())
		return;
	delete graphics_2d_context_;
	graphics_2d_context_ = NULL;
}

void ArtilleryGame::FlushPixelBuffer()
{
	if (!IsContextValid())
		return;
	// Note that the pixel lock is held while the buffer is copied into the
	// device context and then flushed.
	graphics_2d_context_->PaintImageData(*pixel_buffer_, pp::Point());
	if (flush_pending())
		return;
	set_flush_pending(true);
	graphics_2d_context_->Flush(pp::CompletionCallback(&FlushCallback, this));
}

void* ArtilleryGame::FireShell(void* param)
{
	srand((unsigned int) time(NULL));
	ArtilleryGame* artillery_game = static_cast<ArtilleryGame*>(param);
	uint32_t* pixel_bits;
	do {
		ScopedPixelLock scoped_pixel_lock(artillery_game);
		pixel_bits = scoped_pixel_lock.pixels();
	} while (pixel_bits == NULL);

	#define pixel(x, y) pixel_bits[artillery_game->width() * (y) + (x)]

	ScopedPixelLock scoped_pixel_lock(artillery_game);
	vector<int> LandScape(artillery_game->width());
	MakeLandScape(LandScape, artillery_game->height());
	for (int px = 0; px < artillery_game->width(); ++px)
	{
		for (int py = 0; py < artillery_game->height(); ++py)
		{
			uint32_t color = (py < LandScape[px]) ? kblue : kgreen;
			pixel(px , py) = color;
		}
	}

	for (int px = 72; px < 80; ++px)
	{
		for (int py = LandScape[px]-8; py < LandScape[px]; ++py)
		{
			pixel(px , py) = kred;
		}
	}


	pp::CompletionCallback callback(&PaintCallback, artillery_game);
	pp::Module::Get()->core()->CallOnMainThread(0, callback);
	return 0;

}

/// The Module class.  The browser calls the CreateInstance() method to create
/// an instance of your NaCl module on the web page.  The browser creates a new
/// instance for each <embed> tag with type="application/x-nacl".
class ArtilleryGameModule: public pp::Module
{
public:
	ArtilleryGameModule() :
			pp::Module()
	{
	}
	virtual ~ArtilleryGameModule()
	{
	}

	/// Create and return a ArtilleryGameInstance object.
	/// @param[in] instance The browser-side instance.
	/// @return the plugin-side instance.
	virtual pp::Instance* CreateInstance(PP_Instance instance)
	{
		return new ArtilleryGame(instance);
	}
};

} // namespace artillery_game

namespace pp
{
/// Factory function called by the browser when the module is first loaded.
/// The browser keeps a singleton of this module.  It calls the
/// CreateInstance() method on the object you return to make instances.  There
/// is one instance per <embed> tag on the page.  This is the main binding
/// point for your NaCl module with the browser.
Module* CreateModule()
{
	return new artillery_game::ArtilleryGameModule();
}
} // namespace pp
